package com.gengzp.common.utils;

import com.gengzp.common.context.TraceContextHolder;
import com.gengzp.common.model.log.req.CreateTraceLogReq;
import com.gengzp.common.utils.HttpRequestUtils;
import com.gengzp.rpc.utils.RpcUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import jakarta.annotation.PostConstruct;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName TraceUtils
 * @Description 链路追踪工具类（仅支持10秒定时日志落库）
 * @Author gengzp
 * @Date 2025/9/29 10:06
 */
@Component
public class TraceUtils {

    private static final Logger logger = LoggerFactory.getLogger(TraceUtils.class);

    private static Environment environment;

    /**
     * 同步日志锁：确保定时任务执行线程安全
     */
    private static final Object SAVE_LOG_LOCK = new Object();

    /**
     * 链路追踪日志全局队列（key: traceId，value: 对应日志队列）
     * 队列容量1000：避免单traceId日志过多导致内存溢出
     */
    private static final ConcurrentHashMap<String, BlockingQueue<String>> traceLogs = new ConcurrentHashMap<>();
    private static final int QUEUE_CAPACITY = 1000;

    /**
     * 链路追踪日志创建uri
     */
    private static final String CREATE_TRACE_LOG_URI = "/log/trace/create";

    /**
     * 日志同步服务的服务名 (系统服务)
     */
    private static final String LOG_SERVICE_NAME = "system";

    /**
     * 日志落库线程名
     */
    private static final String SYNC_LOG_THREAD_NAME = "save-trace-logs-thread";

    /**
     * 定时任务间隔：10秒（与原逻辑一致）
     */
    private static final long SYNC_INTERVAL = 10000L;

    private TraceUtils(Environment environment) {
        TraceUtils.environment = environment;
    }

    /**
     * 初始化定时日志落库线程（10秒执行一次）
     */
    @PostConstruct
    public void init() {
        Thread thread = new Thread(() -> {
            logger.info("链路追踪日志定时落库线程启动完成，同步间隔：{}ms", SYNC_INTERVAL);
            while (true) {
                try {
                    Map<String, List<String>> logsToSave = Maps.newHashMap();

                    synchronized (SAVE_LOG_LOCK) {
                        // 1. 读取所有非空日志队列，转移到待保存集合（清空原队列）
                        Map<String, BlockingQueue<String>> currentLogs = TraceUtils.getTraceLogs();
                        for (Map.Entry<String, BlockingQueue<String>> entry : currentLogs.entrySet()) {
                            BlockingQueue<String> queue = entry.getValue();
                            if (CollectionUtils.isEmpty(queue)) {
                                continue;
                            }
                            // 转移队列所有元素到List（不阻塞，确保定时执行）
                            List<String> logList = Lists.newArrayList();
                            queue.drainTo(logList);
                            if (CollectionUtils.isNotEmpty(logList)) {
                                logsToSave.put(entry.getKey(), logList);
                            }
                        }

                        // 2. 无日志时，休眠指定间隔；有日志则立即落库
                        if (MapUtils.isEmpty(logsToSave)) {
                            try {
                                SAVE_LOG_LOCK.wait(SYNC_INTERVAL);
                            } catch (InterruptedException ignored) {
                                Thread.currentThread().interrupt(); // 保留中断状态
                            }
                            continue;
                        }
                    }

                    // 3. 批量落库日志（异步执行，不阻塞定时线程）
                    saveTraceLogs(logsToSave);

                } catch (Exception e) {
                    logger.error("定时落库链路追踪日志异常", e);
                    // 异常后休眠1秒，避免频繁报错
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        }, SYNC_LOG_THREAD_NAME);
        thread.setDaemon(true); // 守护线程：应用退出时自动停止
        thread.start();
    }

    /**
     * 添加链路追踪日志（线程安全，队列满时静默丢弃多余日志）
     *
     * @param traceId 链路追踪id
     * @param log     日志内容
     */
    public static void addTraceLog(String traceId, String log) {
        try {
            // 1. 校验参数非空
            if (StringUtils.isBlank(traceId) || StringUtils.isBlank(log)) {
                return;
            }

            // 2. 获取或创建队列（容量1000）
            BlockingQueue<String> queue = traceLogs.computeIfAbsent(traceId,
                    k -> new ArrayBlockingQueue<>(QUEUE_CAPACITY));

            // 3. 队列未满则添加，满则丢弃（避免阻塞业务线程）
            if (!queue.offer(log)) {
                logger.warn("traceId: {} 的日志队列已达最大容量{}，丢弃日志：{}", traceId, QUEUE_CAPACITY, log);
            }

            // 4. 清理空队列对应的key（避免内存泄漏）
            traceLogs.entrySet().removeIf(entry -> entry.getValue().isEmpty());

        } catch (Exception e) {
            logger.error("添加链路追踪日志异常，traceId: {}", traceId, e);
        }
    }

    /**
     * 获取全部链路日志（仅定时任务内部使用）
     */
    private static Map<String, BlockingQueue<String>> getTraceLogs() {
        return traceLogs;
    }

    /**
     * 批量保存链路追踪日志（定时任务核心方法）
     *
     * @param traceLogs 待落库日志（key: traceId，value: 日志列表）
     */
    private static void saveTraceLogs(Map<String, List<String>> traceLogs) {
        // 异步落库：避免阻塞定时线程，不影响下一次同步
        new Thread(() -> {
            if (MapUtils.isEmpty(traceLogs)) {
                return;
            }

            try {
                // 获取日志服务地址（仅获取一次，避免重复RPC调用）
                String systemServiceAddress = RpcUtils.getAddressByServiceName(LOG_SERVICE_NAME);
                if (StringUtils.isBlank(systemServiceAddress)) {
                    logger.error("未找到日志服务实例，无法落库日志");
                    return;
                }

                // 批量处理每个traceId的日志
                for (Map.Entry<String, List<String>> entry : traceLogs.entrySet()) {
                    String traceId = entry.getKey();
                    List<String> logContents = entry.getValue();
                    if (CollectionUtils.isEmpty(logContents)) {
                        continue;
                    }

                    // 组装请求参数
                    CreateTraceLogReq req = new CreateTraceLogReq();
                    req.setTraceId(traceId);
                    req.setServiceName(environment.getProperty("spring.application.name"));
                    req.setLogContents(logContents);

                    // 发送落库请求（忽略单次失败，避免影响其他日志）
                    try {
                        HttpRequestUtils.post(
                                "http://" + systemServiceAddress + CREATE_TRACE_LOG_URI,
                                req);
                        logger.debug("traceId: {} 日志落库成功，共{}条", traceId, logContents.size());
                    } catch (Exception e) {
                        logger.error("traceId: {} 日志落库失败", traceId, e);
                    }
                }
            } catch (Exception e) {
                logger.error("批量落库日志异常", e);
            }
        }, SYNC_LOG_THREAD_NAME + "-async").start();
    }

    /**
     * 获取当前请求的 traceId
     */
    public static String getCurrentTraceId() {
        return TraceContextHolder.getTraceId();
    }

}